package com.kly.sc.api.application.controller.auth;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.config.annotation.NacosValue;
import com.fqgj.base.services.redis.RedisClient;
import com.fqgj.common.api.Response;
import com.fqgj.common.utils.CollectionUtils;
import com.fqgj.common.utils.StringUtils;
import com.kly.dto.ProductDto;
import com.kly.dto.TOrderDto;
import com.kly.dto.UserVasPurchasesDto;
import com.kly.enums.OrderStatus;
import com.kly.enums.ProductCategory;
import com.kly.sc.api.application.annos.SignIgnore;
import com.kly.sc.api.application.annos.VisitorAccessible;
import com.kly.sc.api.application.constant.ApiRedisKeys;
import com.kly.sc.api.application.controller.appEvaluate.response.EvaluateRes;
import com.kly.sc.api.application.controller.auth.response.MessageRes;
import com.kly.sc.api.application.enums.ApiResultStatus;
import com.kly.sc.api.application.helper.RequestLocalInfo;
import com.kly.sc.api.application.utils.JwtUtils;
import com.kly.sc.api.application.utils.Result;
import com.kly.sc.api.application.utils.RpcUtils;
import com.kly.sc.api.infras.app.RequestBasicInfo;
import com.kly.service.MemberService;
import com.kly.service.ProductService;
import com.kly.service.TOrderService;
import com.kly.service.UserVasPurchasesService;
import com.kly.service.pay.GooglePayService;
import com.kly.user.constant.RedisKey;
import com.kly.user.dto.UserAccessDto;
import com.kly.user.dto.UserBaseDto;
import com.kly.user.dto.UserLoginDto;
import com.kly.user.enums.*;
import com.kly.user.service.AuthLoginService;
import com.kly.user.service.OtherService;
import com.kly.user.service.UserService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.*;

@Slf4j
@RestController
@RequestMapping("/api/auth")
public class AuthLoginController {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    public static final Set<String> WHITE_LIST = new HashSet<>(Arrays.asList("16505551234","16501115555"));

    @NacosValue(value = "${white.list}", autoRefreshed = true)
    private String whiteList;
    @NacosValue(value = "${meboo.logout.notice.message}", autoRefreshed = true)
    private String mebooLogoutMessage;
    @NacosValue(value = "${meboo.login.message}", autoRefreshed = true)
    private String mebooLoginMessage;
    @NacosValue(value = "${touchu.logout.notice.message}", autoRefreshed = true)
    private String touchuLogoutMessage;
    @NacosValue(value = "${touchu.login.message}", autoRefreshed = true)
    private String touchuLoginMessage;
    @Resource
    ProductService productService;
    @Resource
    AuthLoginService authLoginService;
    @Resource
    UserVasPurchasesService userVasPurchasesService;
    @Resource
    UserService userService;
    @Resource
    RedisClient redisClient;
    @Resource
    TOrderService orderService;
    @Resource
    GooglePayService googlePayService;
    @Resource
    private MemberService memberService;
    @Resource
    OtherService otherService;

    private static final Integer ONE_DAYS = 1;
    private static final Integer THREE_DAYS = 3;


    @RequestMapping("/login")
    @SignIgnore
    @VisitorAccessible
    public Response authLogin(@Valid @RequestBody UserLoginDto userLogin, BindingResult results) {
//        logger.info("AuthLoginController.authLogin params:{}", JSONObject.toJSONString(userLogin));
        final String appCode = RequestLocalInfo.getRequestBasicInfo().getAppCode();
        final String appSource = RequestLocalInfo.getRequestBasicInfo().getAppSource();
        userLogin.setAppCode(appCode);
        if (results.hasErrors()) {
            return Response.error(ResultStatus.ILLEGAL_PARAMS);
        }

        if(StringUtils.isNotEmpty(userLogin.getEmail())) {
            String booleanLoginBlack = redisClient.hGet(RedisKey.USER_BLACK, userLogin.getEmail());
            if (StringUtils.isNotEmpty(booleanLoginBlack)) {
                return Response.error(ResultStatus.ACCOUNT_SUSPENDED);
            }
        }
        if(StringUtils.isNotEmpty(userLogin.getMobile())) {
            String booleanLoginBlack = redisClient.hGet(RedisKey.USER_BLACK, userLogin.getMobile());
            if (StringUtils.isNotEmpty(booleanLoginBlack)) {
                return Response.error(ResultStatus.ACCOUNT_SUSPENDED);
            }
        }


        //如果有验证码并且不在白名单内的，先验证码校验
        if(StringUtils.isNotEmpty(userLogin.getMobile())) {
            String whiteUser = redisClient.hGet(ApiRedisKeys.WHITE_USER.getValue(), userLogin.getMobile());
            if (StringUtils.isEmpty(whiteUser) &&StringUtils.isNotEmpty(userLogin.getVerificationCode())) {
                String verifyCode = redisClient.getString(ApiRedisKeys.PHONE_VERIFICATION_CODE.getValue(), appCode, userLogin.getMobile());
                //验证码不正确或验证码过期 返回报错
                if(StringUtils.isEmpty(verifyCode)|| !verifyCode.equals(userLogin.getVerificationCode())){
                    return Response.error(ApiResultStatus.VERIFICATION_CODE_IS_INVALID);
                }
            }
        }


        RequestBasicInfo requestBasicInfo = RequestLocalInfo.getRequestBasicInfo();
        userLogin.setAppVersion(requestBasicInfo.getAppVersionName());
        userLogin.setAppVersionNo(Integer.valueOf(requestBasicInfo.getAppVersion()));
        processIosAccount(userLogin);
        userLogin.setAppSource(Integer.valueOf(appSource));
        Response<UserBaseDto> userBaseResponse = authLoginService.authLogin(userLogin);
        UserBaseDto userBase = userBaseResponse.getData();
        if (!userBaseResponse.isSuccess() || userBase == null) {
            return userBaseResponse;
        }

        redisClient.set(ApiRedisKeys.USER_LOGIN_TYPE.getValue(), userLogin.getLoginType(),
                userBaseResponse.getData().getUserCode());

        userBase.setAvatarUrl(otherService.getFullImageUrl(userBase.getAvatarUrl()));


        // 首次注册
        if (userBase.getFirstRegister()) {
            if (userBase.getNewUserFlag()) {
                // 设置新用户标记
                setNewUserFlag(userBase);
                // 新注册用户授予权限
                addBenefits(userLogin, userBase, ProductCategory.NEW_USER_BENEFITS.getCode());
            } else {
                // 老用户登陆后，由系统自动生成权益
                addBenefits(userLogin, userBase, ProductCategory.OLD_USER_BENEFITS.getCode());
            }
        }
        // 非首次注册
        else {
            addBenefits(userLogin, userBase, ProductCategory.OLD_USER_BENEFITS.getCode());
        }


        String token = JwtUtils.generateToken(userBaseResponse.getData().getUserCode(), null, true);
        redisClient.set("key_{}_{}", token, RememberTime.ONE_YEAR.getTime(), userBaseResponse.getData().getUserCode(), RedisKey.TOKEN_CODE);
        userBaseResponse.getData().setToken(token);
        UserAccessDto userAccessDto = new UserAccessDto();
        userAccessDto.setToken(token);
        userAccessDto.setUserCode(userBaseResponse.getData().getUserCode());
        authLoginService.saveToken(userAccessDto, UserTokenType.SYSTEM_TOKEN);
        return userBaseResponse;

    }

    /**
     * ios的登陆凭证单独处理
     */
    private void processIosAccount(@RequestBody @Valid UserLoginDto userLogin) {
        if (LoginType.IOS.getType() == userLogin.getLoginType()) {
            userLogin.setIdentityToken(userLogin.getAccount());
            userLogin.setAccount(SecureUtil.md5(userLogin.getAccount()));
        }
    }

    private void addBenefits(UserLoginDto userLogin, UserBaseDto userBase, int code) {
        // 白名单用户使用新用户权益
        if (StringUtils.isNotEmpty(userLogin.getEmail()) && whiteList.contains(userLogin.getEmail())) {
            code = ProductCategory.NEW_USER_BENEFITS.getCode();
        }

        // 已经发放过权益不再进行发放
        final List<UserVasPurchasesDto> existsPurchases = RpcUtils.getResult(userVasPurchasesService.getAllUserBenefits(userBase.getUserCode()));
        if (CollectionUtils.isNotEmpty(existsPurchases)) {
            return;
        }

        Response<List<ProductDto>> products = productService.queryProductsByProductCategory(userLogin.getAppCode(), code);
        if (CollUtil.isNotEmpty(products.getData())) {
            userVasPurchasesService.saveUserPurchasesBenefits(userBase.getUserCode(), products.getData().get(0));
            logger.info("saveUserPurchasesBenefits params:{}", JSONUtil.parseArray(products.getData()));
        }
    }

    private void setNewUserFlag(UserBaseDto userBase) {
        // 获取1天后0点的时间
        DateTime endTimeInOneDays = DateUtil.beginOfDay(DateUtil.offsetDay(DateUtil.date(), ONE_DAYS));
        // 获取当前时间
        DateTime nowTime = DateUtil.date();
        long seconds = DateUtil.between(nowTime, endTimeInOneDays, DateUnit.SECOND, false);

        //如果是新用户则添加新用户标记，截止时间为第三天0点
        redisClient.set(ApiRedisKeys.NEW_USER.getValue(), ApiRedisKeys.NEW_USER.getKey(), seconds, userBase.getUserCode());
    }

    @RequestMapping("/logout")
    public Response logOut() {
        String token = RequestLocalInfo.getRequestBasicInfo().getToken();
        String userCode = JwtUtils.getAuthentication(token).getName();
        userService.addLogoutRecord(userCode);

        String booleanLoginOut = redisClient.hGet(RedisKey.USER_LOGIN_OUT, userCode);
        if(StringUtils.isNotEmpty(booleanLoginOut)) {
            authLoginService.logout(userCode);
            final Response<String> removeAccountResponse = userService.removeUser(userCode);
            // 账号删除成功，如果存在会员订阅记当，需要取消会员订阅
            if (Objects.nonNull(removeAccountResponse) && removeAccountResponse.isSuccess()) {
                final TOrderDto order = RpcUtils.getResult(orderService.queryLatestSubscriptionsOrderByUserCode(userCode));
                log.info("====removeAccount, userCode:{}, orderInfo:{}", userCode, JSONObject.toJSONString(order));

                if (Objects.isNull(order) || OrderStatus.CLOSE.getStatus() == order.getStatus()) {
                    return Response.ok();
                }

                final String purchaseToken = order.getPurchaseToken();
                log.info("====removeAccount, userCode:{}, purchaseToken:{}", userCode, purchaseToken);

                if (StringUtils.isNotEmpty(purchaseToken)) {
                    String appCode = RequestLocalInfo.getRequestBasicInfo().getAppCode();
                    final ProductDto productDto = RpcUtils.getResult(productService.queryProductsByProductCode(appCode, order.getProductCode()));
                    final String productConfig = productDto.getProductConfig();
                    final JSONObject configs = JSONObject.parseObject(productConfig);

                    final Boolean cancel = RpcUtils.getResult(googlePayService.cancelSubscription(configs.getString("packageName"),
                            configs.getString("purchaseProductName"), purchaseToken));
                    log.info("====removeAccount, userCode:{}, cancelSubscription:{}", userCode, cancel);

                    if (cancel) {
                        // 取消权益
                        memberService.offAutoRenew(userCode);

                        //修改会员信息后，删除会员缓存
                        redisClient.del(ApiRedisKeys.USER_MEMBER.getValue(), userCode);
                        log.info("===offAutoRenewSuccess===, userCode:{}, orderNo:{}", userCode, order.getOrderNo());
                        return Response.ok();
                    }
                }
            }
        }
        return Response.ok();

    }


    @GetMapping("/message")
    @ApiOperation(value = "注销账户提示文案", notes = "注销账户提示文案")
    public Result<MessageRes> message() {
        MessageRes messageRes = new MessageRes();
        final String appCode = RequestLocalInfo.getRequestBasicInfo().getAppCode();
        if (AppCodeEnum.MEBOO.getAppCode().equals(appCode)) {
            messageRes.setNoticeMessage(mebooLogoutMessage);
            messageRes.setLoginMessage(mebooLoginMessage);
        } else {
            messageRes.setNoticeMessage(touchuLogoutMessage);
            messageRes.setLoginMessage(touchuLoginMessage);
        }
        return Result.ok(messageRes);
    }

    @GetMapping("/checkLogout")
    @ApiOperation(value = "是否是注销账户", notes = "是否是注销账户")
    public Result<String> checkLogout(@RequestParam @ApiParam("userCode") String userCode) {
        String result = "";
        int count = userService.checkLogout(userCode);
        if (count > 0) {
            result = "Go ahead and try some daring\n" +
                    "dates!\uD83D\uDE0D";
        }
        return Result.ok(result);
    }

}
